<?php
	/**
	 * Parses PHP file, without including them.
	 * @author IMP
	 */

	class PHPFileParser {		
		private $inClass = false,
				$classBraceLevel = 0,
				$inFunction = false,
				$functionBraceLevel = 0,
				$classes = array(),
				$functions = array(),
				$methodStack = array(),
				$previousToken = null,
				$outterStack = array(),
				$outterKeys = array(),
				$braceLevel = 0,
				$modifierStack = array();
		
		public function __construct(File $phpFile){
			$tokens = token_get_all($phpFile->read());
			
			foreach($tokens as $id => $token){
				if(is_array($token)){
					$key = $token[0];
					$content = $token[1];
					
					if($key == T_WHITESPACE) continue;

					if($key == T_STRING && $this->previousToken != null){
						$this->handleStringToken($this->previousToken, $content);
						$this->previousToken = null;
						continue;
					}
					
					if(in_array($key, array(T_CLASS, T_FUNCTION, T_IMPLEMENTS, T_EXTENDS))){
						$this->previousToken = $key;
						continue;
					}
					
					if(in_array($key, array(T_PROTECTED, T_PRIVATE, T_PUBLIC, T_STATIC, T_FINAL, T_ABSTRACT))){
						$this->modifierStack[] = $key;
						continue;
					}
					
					if(!$this->inClass && !$this->inFunction){
						$this->outterStack[] = array($key, $content);
						$this->outterKeys[$key] = true;
						continue;
					}
				}else{
					if($token == '{'){
						$this->braceLevel++;
					}
					
					if($token == '}'){
						$this->braceLevel--;
						
						if($this->braceLevel == $this->functionBraceLevel && $this->inFunction){
							$this->inFunction = false;
						}elseif($this->braceLevel == $this->classBraceLevel && $this->inClass && !$this->inFunction){
							$this->inClass = false;
							$this->classes[count($this->classes) - 1]->pushMethods($this->methodStack);
							$this->methodStack = array();
						}
						
						continue;
					}
					
					if(!$this->inClass && !$this->inFunction){
						$this->outterStack[] = array(T_CHARACTER, $token);
						$this->outterKeys[T_CHARACTER] = true;
					}
				}
			}
		}
		
		private function handleStringToken($previous, $value){
			if($previous == T_CLASS){
				$this->classes[] = new PHPClass($value, $this->modifierStack);
				$this->inClass = true;
				$this->classBraceLevel = $this->braceLevel;
				$this->modifierStack = array();
			}elseif($previous == T_FUNCTION && $this->inClass){
				$this->methodStack[] = new PHPFunction($value, $this->modifierStack);
				$this->inFunction = true;
				$this->functionBraceLevel = $this->braceLevel;
				$this->modifierStack = array();
			}elseif($previous == T_FUNCTION){
				$this->functions[] = new PHPFunction($value);
				$this->inFunction = true;
				$this->functionBraceLevel = $this->braceLevel;
				$this->modifierStack = array();
			}elseif($previous == T_EXTENDS && $this->inClass){
				$this->classes[count($this->classes) - 1]->setParent($value);
			}elseif($previous == T_IMPLEMENTS && $this->inClass){
				$this->classes[count($this->classes) - 1]->addImplementation($value);
			}
		}
		
		public function definesClass($class){
			foreach($this->classes as $c){
				if($c->getName() == $class) return true;
			}
			
			return false;
		}
		
		public function getClass($class){
			foreach($this->classes as $c){
				if($c->getName() == $class) return $c;
			};
		}
		
		public function getClasses(){
			return $this->classes;
		}
		
		public function getFunctions(){
			return $this->functions;
		}
		
		public function getOutterClassTokens(){
			return $this->outterStack;
		}
		
		public function getOutterClassTokenIDs(){
			return array_keys($this->outterKeys);
		}
	} 
?>